package miner

import (
	block "blockChain/singleBlock"
	"fmt"

	"time"

	"github.com/fatih/color"
)

// 类似c++的宏定义
const (
	reportInterval = 6
	debugInterval  = 3
)

// 随意发布区块
func (n *Node) InterfereRun() {
	fmt.Println("start node : ", n.id)
	defer fmt.Printf("node %d: (干扰)退出\n", n.id)
	nextBlock := block.Block{}
	nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)

	ticker := time.NewTicker(time.Second * reportInterval)
	defer ticker.Stop()

loop:
	for {
		select {
		case <-n.NotifyChan:
			{
				break loop
			}
		case <-ticker.C:
			{
				fmt.Printf("node %d: 发布新区块\n", n.id)
				n.Broadcast(
					Message{
						sender:    n.id,
						newBlock:  nextBlock,
						dishonest: true,
					},
				)
			}
		}
	}
}

// 诚实节点：在最长链上进行构造
func (n *Node) HonestRun() {
	fmt.Println("start node : ", n.id)
	if n.longestChainTail.Height == block.CIRCLE ||
		n.longestChainTail.Height == block.UNKNOWN_HEIGHT {
		fmt.Printf("node %d: 无创世块\n", n.id)
		return
	}
	defer fmt.Printf("node %d: 退出\n", n.id)

	nextBlock := block.Block{}
	nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)

	// 每3秒，检查当前区块的状态
	deBugticker := time.NewTicker(time.Second * debugInterval)

	// 每隔10s，诚实节点发送当前自己的最长链，以进行同步
	ticker := time.NewTicker(time.Second * reportInterval)
	defer ticker.Stop()

	// 仅用于测试：检测区块链的实际长度
	defer func() {
		tmpBufferPool := make(map[string]block.Block)
		for blockID, tmpBlock := range n.bufferPool {
			tmpBlock.Height = block.UNKNOWN_HEIGHT
			tmpBufferPool[blockID] = tmpBlock
		}
		realLen := block.GetLongestChainTail(tmpBufferPool)
		if realLen == n.longestChainTail {
			fmt.Println("node", n.id, ": 区块链、缓冲区一致√，最长", realLen.Height)
		} else {
			fmt.Println("node", n.id, ": 区块链、缓冲区不一致，实际最长", realLen.Height)
		}
	}()

loop:
	for {
		select {
		// 1. 有无中止信号
		case <-n.NotifyChan:
			{
				break loop
			}
		// 2. 其他矿工发来新区快
		case msg := <-n.receiveChan:
			{
				if !msg.newBlock.IsValid() {
					fmt.Printf("node %d: 其他节点有新区块，不合格from %d\n", n.id, msg.sender)
					break
				}
				if msg.isRecord {
					// 不是新区块，忽略
					break
				}
				msg.newBlock.Height = block.UNKNOWN_HEIGHT
				n.bufferPool[msg.newBlock.SelfHash] = msg.newBlock
				msg.newBlock.Height = block.CalculateHeight(n.bufferPool, msg.newBlock.SelfHash)
				if n.longestChainTail.Height == block.CIRCLE ||
					n.longestChainTail.Height == block.UNKNOWN_HEIGHT ||
					n.longestChainTail.Height < msg.newBlock.Height {
					n.longestChainTail = n.bufferPool[msg.newBlock.SelfHash]
					nextBlock = block.Block{}
					nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
				}
			}
		// (仅用于测试)每隔3s，显示当前缓冲区
		case <-deBugticker.C:
			{
				fmt.Printf("node %d: 总区块数量%d, 最长%d\n", n.id, len(n.bufferPool), n.longestChainTail.Height)
			}
		// (仅用于测试)诚实节点汇报自己的最长链
		case <-ticker.C:
			{
				n.Broadcast(Message{
					sender:    n.id,
					newBlock:  n.longestChainTail,
					dishonest: false,
					isRecord:  true,
				})
			}

		// 3. 安心挖矿
		default:
			{
				raw := fmt.Sprintf("%s%d", nextBlock.Previous, nextBlock.Nonce)
				check := block.CalculateHash([]byte(raw))
				if check <= nextBlock.BitsThreshold {
					nextBlock.Height = n.longestChainTail.Height + 1
					nextBlock.SetTime(time.Now().Format("15:04:05"))
					fmt.Printf("node %d: 挖矿成功，新区块{prev: %s self: %s nonce: %d}\n", n.id, nextBlock.Previous, nextBlock.SelfHash, nextBlock.Nonce)
					n.Broadcast(Message{
						sender:    n.id,
						newBlock:  nextBlock,
						dishonest: false,
					})
					n.bufferPool[nextBlock.SelfHash] = nextBlock

					n.longestChainTail = nextBlock
					nextBlock = block.Block{}
					nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
				} else {
					nextBlock.Nonce++
				}
			}
		}
	}
}

// 只在自己的最长链上构造区块，忽略全局最长链
// badWorkers为恶意节点的总数
func (n *Node) DivertRun(badWorkers int) {
	fmt.Println("start node : ", n.id)
	defer color.Red("node %d: 退出", n.id)
	nextBlock := block.Block{}
	nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
	nextBlock.SetSolution(n.id % uint64(badWorkers))

	// 检测缓冲区有无变动
	// changed := make(chan struct{})
	ticker := time.NewTicker(time.Second * debugInterval)

	// 仅用于测试：检测区块链的实际长度
	defer func() {
		tmpBufferPool := make(map[string]block.Block)
		for blockID, tmpBlock := range n.bufferPool {
			tmpBlock.Height = block.UNKNOWN_HEIGHT
			tmpBufferPool[blockID] = tmpBlock
		}
		realLen := block.GetLongestChainTail(tmpBufferPool)
		if realLen.Height == n.longestChainTail.Height {
			color.Green("node %d: 区块链、缓冲区一致√，最长%d", n.id, realLen.Height)
		} else {
			color.Red("node %d: 区块链、缓冲区不一致，实际最长%d", n.id, realLen.Height)
		}
	}()

loop:
	for {
		select {
		// 1. 运行结束
		case <-n.NotifyChan:
			{
				break loop
			}
		// 2. 其他矿工发来新区快
		case msg := <-n.receiveChan:
			{
				if !msg.newBlock.IsValid() {
					color.Red("node %d: 其它节点有新区块，不合格from %d", n.id, msg.sender)
					break
				}
				if !msg.dishonest {
					// 诚实节点当前的最长链，不是新区块，不用放入缓冲区
					if msg.isRecord {
						_, exists := n.bufferPool[msg.newBlock.SelfHash]
						if exists {
							color.Red("node %d: 诚实节点区块被同步", n.id)
							break loop
						}
						break
					}
					// 诚实节点的新区块，忽略
					color.Red("node %d: 诚实节点有新区块，忽略from %d", n.id, msg.sender)
					break
				}
				// color.Red("node %d: 恶意节点有区块，合格from %d", n.id, msg.sender)
				msg.newBlock.Height = block.UNKNOWN_HEIGHT
				n.bufferPool[msg.newBlock.SelfHash] = msg.newBlock
				msg.newBlock.Height = block.CalculateHeight(n.bufferPool, msg.newBlock.SelfHash)
				if n.longestChainTail.Height == block.CIRCLE ||
					n.longestChainTail.Height == block.UNKNOWN_HEIGHT ||
					n.longestChainTail.Height < msg.newBlock.Height {
					n.longestChainTail = n.bufferPool[msg.newBlock.SelfHash]
					nextBlock = block.Block{}
					nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
					nextBlock.SetSolution(n.id % uint64(badWorkers))
				}
			}
		// (仅用于测试)每隔3s输出
		case <-ticker.C:
			{
				color.Magenta("node %d: 总区块数量%d, 最长%d", n.id, len(n.bufferPool), n.longestChainTail.Height)
			}
		// 3. 安心挖矿
		default:
			{
				raw := fmt.Sprintf("%s%d", nextBlock.Previous, nextBlock.Nonce)
				check := block.CalculateHash([]byte(raw))
				if check <= nextBlock.BitsThreshold {
					nextBlock.SetTime(time.Now().Format("15:04:05"))
					color.Red("node %d: 挖矿成功，新区块{prev: %s self: %s nonce: %d}", n.id, nextBlock.Previous, nextBlock.SelfHash, nextBlock.Nonce)
					n.Broadcast(Message{
						sender:    n.id,
						newBlock:  nextBlock,
						dishonest: true,
					})
					nextBlock.Height = n.longestChainTail.Height + 1
					n.bufferPool[nextBlock.SelfHash] = nextBlock
					n.longestChainTail = nextBlock
					nextBlock = block.Block{}
					nextBlock.Initialize(n.longestChainTail.SelfHash, n.OfficialThreshold)
					nextBlock.SetSolution(n.id % uint64(badWorkers))
				} else {
					nextBlock.Nonce += uint64(badWorkers)
				}
			}
		}
	}
}
